#include "Kernel.h"
#include "Shell.h"
#include "Drivers/Console.h"
#include "Drivers/Keyboard.h"
#include "Utils/String.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/Thread.h"
#include <Mint/Keys.h>

CShell sShell;
CShell * Shell = &sShell;

void CShell::Initialize( ITextInput * input, ITextOutput * output )
{
	ASSERT( input != NULL );
	ASSERT( output != NULL );

	SHELL_TRACE( "Initializing...\n" );

	fInput = input;
    fOutput = output;
	fPosition = 0;
	fRunning = false;
	fThread = NULL;

	Memory_Set( &fBuffer, 0, SHELL_BUFFER_LENGTH );
}

void CShell::Shutdown( void )
{
	SHELL_TRACE( "Shutting down...\n" );

	fRunning = false;
	delete fThread;
}


void CShell::Start( void )
{
	SHELL_TRACE( "Running...\n" );

	fRunning = true;
	fThread = new Thread( "Shell", &CShell::__Run, this );

	Scheduler::StartThread( sThread );
}


int CShell::__Run( void * argument )
{
	ASSERT( argument != NULL );

	CShell * shell = (CShell *)argument;
	return shell->_Run();
}


int CShell::_Run( void )
{
	while( fRunning )
	{
		Scheduler::Yield();

		fOutput->Write( "\n\xFB\xF3Mi\xF2nt\xFF: " );
		_ReadCommand();
	}

	return 0;
}


void CShell::_ReadCommand( void )
{
	while( fRunning )
	{
		if( fInput->CanRead() != true )
		{
			Scheduler::Yield();
			continue;
		}

        char keyCode = fInput->Read();

		if( keyCode == (char)KEY_ENTER && fPosition > 0 )
		{
			fOutput->Write( '\n' );
			_ProcessCommand();
			break;
		}

		else if( keyCode == (char)KEY_BACKSPACE && fPosition > 0 )
		{
			fOutput->Write( '\b' );
			fBuffer[--fPosition] = 0;
		}

		else if( IsPrintable( keyCode ) && fPosition < SHELL_BUFFER_LENGTH - 1 )
		{
			fOutput->Write( keyCode );
			fBuffer[fPosition++] = keyCode;
		}
	}
}


void CShell::_ProcessCommand( void )
{
	// TEMP: this whole hard coding of commands will be removed later
	if( StringCompare( "hi", &fBuffer[0], true, 3 ) == 0 )
		fOutput->Write( "\nHello.\n" );

	else if( StringCompare( "heap", &fBuffer[0], 4 ) == 0 )
	{
	    Hardware->Lock();
		fOutput->Write( "\n\xFB\xF3Heap Information\xFF: (in bytes)\n" );
		fOutput->Write( "\t\xFB Total\xFF: %8u \xFB Available\xFF: %8u \xFB Burnt\xFF: %8u\n", Heap->GetSize(), Heap->GetAvailable(), Heap->GetUsed() );
		fOutput->Write( "\t\xFB Used\xFF:  %8u \xFB Allocated\xFF: %8u \xFB Freed\xFF: %8u\n", Heap->GetAllocated(), Heap->GetFreed() );
	    Hardware->Unlock();
	}

	else if( StringCompare( "threads", &fBuffer[0], true, 7 ) == 0 )
	{
	    Hardware->Lock();
		fOutput->Write( "\n\xFB\xF3Thread Information\xFF:\n" );

		List * threads = Scheduler::RunningThreads;

		int totalThreads = threads->GetCount();

        if( Scheduler::CurrentThread != NULL )
        {
            fOutput->Write( "\t\xFBThread\xFF: \xF3\xFB%s\xFF (\xFB%p)\n", Scheduler::CurrentThread->GetName(), Scheduler::CurrentThread );
            totalThreads++;
        }

        for( ListNode * node = threads->GetFirst(); node != NULL; node = node->GetNext() )
        {
            Thread * thread = (Thread *)node;
            fOutput->Write( "\tThread: \xF3%s\xFF (%p)\n", thread->GetName(), thread );
        }

        fOutput->Write( "\t\xFB%i\xFF total.\n", totalThreads );
	    Hardware->Unlock();
	}

	else if( StringCompare( "panic", &fBuffer[0], true, 6 ) == 0 )
	{
		PANIC( "Fake panic issued!\n" );
	}

	else if( StringCompare( "int", &fBuffer[0], true, 6 ) == 0 )
	{
		asm( "int $0xFE" );
	}

	else if( StringCompare( "bye", &fBuffer[0], true, 4 ) == 0 )
	{
		fOutput->Write( "\nGoodbye.\n" );
		sRunning = false;
	}

	else if( StringCompare( "Ashley?", &fBuffer[0], true, 7 ) == 0 )
		fOutput->Write( "\nThe most\xF4\xFB beautiful\xFF,\xF1\xFB awesome\xFF,\xF3\xFB smart\xFF and\xF5\xFB sexy\xFF woman in the\xFB whole\xFD world!\n" );

	else
		fOutput->Write( "\nUnknown Command.\n" );

	Memory_Set( &fBuffer[0], 0, fPosition );
	fPosition = 0;
}
